﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;

namespace WHLRDF.ORM
{
    public class DataProxy:IDisposable
    {
        /// <summary>
        /// 表名
        /// </summary>
        public virtual string TableName { get; set; }

        /// <summary>
        /// 主键
        /// </summary>
        public virtual string PrimaryKeyName { get; set; }
        /// <summary>
        /// 是否自增
        /// </summary>
        public bool Identifier { get; set; }

        /// <summary>
        /// 组装查询sql
        /// </summary>
        public string QueryFieldBuilder { get; set; }

        /// <summary>
        /// 实体表属性
        /// </summary>
        public TableAttribute TableAttr { get; set; }

        /// <summary>
        /// 属性列表
        /// </summary>
        public List<DbColumnAttribute> DbFields { get; set; }

        /// <summary>
        /// 实体属性
        /// </summary>
        public List<PropertyInfo> EntityFields { get; set; }
        public DataProxy()
        {
            
        }
 
        public DataProxy(Type dbMapType)
        {
            ApplyInstance(dbMapType);
        }
        #region  创建数据访问组件
        /// <summary>
        /// 创建数据访问组件
        /// </summary>
        /// <param name="connection">数据库连接</param>
        /// <returns>数据访问组件</returns>
        IDbRepository CreateDataAccessComponent(IDbRepository dbRepository)
        {
            if (dbRepository == null)
            {
                return AppHttpContext.GetSerivce<IDbRepository>();
            }
            else
            {
                return dbRepository;
            }
        }
        #endregion
        #region 

        /// <summary>
        /// 初始化实体方法
        /// </summary>
        private void ApplyInstance(Type dbMapType)
        {

            if (this.DbFields != null && this.DbFields.Count > 0)
            {
                return;
            }
            var entityFields = new List<PropertyInfo>();
            this.DbFields = ReflectionHelper.getDbColumnProperty(dbMapType, ref entityFields);
            this.EntityFields = entityFields;
            var primarykey = this.DbFields.Where(x => x.IsPrimaryKey).FirstOrDefault();
            if (primarykey != null)
            {
                this.Identifier = primarykey.Identifier;
                if (string.IsNullOrWhiteSpace(this.PrimaryKeyName))
                {
                    this.PrimaryKeyName = primarykey.Name;
                }
            }
            else
            {
                throw new Exception("该表未设置主键");
            }
            SetTableInfo(dbMapType);
            CreateSQL();
        }
        /// <summary>
        /// 生成SQL方法
        /// </summary>
        private void CreateSQL()
        {
            if (this.DbFields == null || this.DbFields.Count <= 0)
            {
                return;
            }
            int index = 0;
            foreach (var field in this.DbFields)
            {
                if (field.Ignore || !field.IsEnabled)
                    continue;
                if (!string.IsNullOrWhiteSpace(field.Name))
                {
                    if (index >= 1)
                    {
                        this.QueryFieldBuilder += ",";
                    }
                    index++;
                    this.QueryFieldBuilder += field.Name;
                }
            }

        }

        /// <summary>
        /// 获取Table属性，并根据属性执行相关操作
        /// </summary>
        /// <param name="t"></param>
        private void SetTableInfo(Type dbMapType)
        {

            this.TableAttr = (TableAttribute)dbMapType.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault();
            if (string.IsNullOrWhiteSpace(this.TableName) && this.TableAttr != null)
            {
                this.TableName = this.TableAttr.Name;
            }
            if (this.DbFields != null && this.DbFields.Count > 0)
            {
                int index = 0;
                if (this.TableAttr != null)
                {
                    if (!this.TableAttr.IsCreated)
                    {
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__CreateBy.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__CreateDate.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                    }
                    if (!this.TableAttr.IsLastModify)
                    {
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__LastModifyDate.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__LastModifyUserId.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                    }
                    if (!this.TableAttr.IsDeleted)
                    {
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__IsDeleted.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }

                    }
                }
            }
        }

        #endregion

        #region CRUD SQL

        /// <summary>
        /// 验证格式是否正确
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, string> CheckValid(EntityBase dbEntityMap)
        {
            Dictionary<string, string> modelState = new Dictionary<string, string>();
            if (dbEntityMap.DataCollection != null && dbEntityMap.DataCollection.Count > 0)
            {
                foreach (var item in dbEntityMap.DataCollection)
                {
                    string strError = "";
                    var dbField = this.DbFields.Where(x => x.Name == item.Key).FirstOrDefault();
                    if (dbField != null)
                    {
                        if (!CheckValidHelper.CheckDbColumn(dbField, item.Value != null ? item.Value.ToString() : "", ref strError))
                        {
                            modelState.Add(item.Key, strError);
                        }

                    }
                }
            }
            return modelState;
        }

        /// <summary>
        /// 获取查询SQL
        /// </summary>
        /// <param name="isQueryPage"></param>
        /// <returns></returns>
        public string SelectSql()
        {
            StringBuilder strsql = new StringBuilder(" select " + this.QueryFieldBuilder + " from " + this.TableName + " where  ");
            if (this.TableAttr.IsDeleted)
            {
                strsql.Append(EntityBase.__IsDeleted + "=0 ");
            }
            else
            {
                strsql.Append(" 1=1 ");
            }
            strsql.Append(" {0} ");
            return strsql.ToString();

        }

        /// <summary>
        /// 构建主键查询sql
        /// </summary>
        /// <param name="model">实体</param>
        /// <param name="dbParameters">返回参数</param>
        /// <returns></returns>
        private string GetPrimaryQueryString(object model,ref DataParameterCollection dbParameters)
        {
            StringBuilder strsql = new StringBuilder(" select 1 from " + this.TableName + " where  ");
            if (this.TableAttr!=null&&this.TableAttr.IsDeleted)
            {
                strsql.Append(EntityBase.__IsDeleted + "=0 ");
            }
            else
            {
                strsql.Append(" 1=1 ");
            }
            strsql.Append(" {0} ");
            object id = GetPrimaryValue(model);
            if (id == null || string.IsNullOrWhiteSpace(id.ToString()))
            {
                return "";
            }
            var criterion = Expression.Eq(this.PrimaryKeyName, id);
            return string.Format(strsql.ToString(), " and " + criterion.ToString(ref dbParameters));
        }
        /// <summary>

        /// <summary>
        /// 新增sql
        /// </summary>
        /// <param name="dbRepository">数据仓库</param>
        /// <param name="model">实体</param>
        /// <param name="parameter">参数</param>
        /// <returns></returns>
        public string InsertSql(IDbRepository dbRepository,object model,  ref DataParameterCollection parameter)
        {
            StringBuilder strsql = new StringBuilder();
            StringBuilder strParam = new StringBuilder();
            strsql.Append(" insert into " + this.TableName);
            strsql.Append("(");
            strParam.Append(" values(");
            int index = 0;
            if (this.TableAttr!=null && this.TableAttr.IsCreated)
            {
                this.EntityFields.Where(x => x.Name == EntityBase.__CreateBy).FirstOrDefault().SetValue(model, ApplicationEnvironments.DefaultSession.UserId);
                this.EntityFields.Where(x => x.Name == EntityBase.__CreateDate).FirstOrDefault().SetValue(model, DateTime.Now);
            }
            if (this.TableAttr != null && this.TableAttr.IsLastModify)
            {
                this.EntityFields.Where(x => x.Name == EntityBase.__LastModifyUserId).FirstOrDefault().SetValue(model, ApplicationEnvironments.DefaultSession.UserId);
                this.EntityFields.Where(x => x.Name == EntityBase.__LastModifyDate).FirstOrDefault().SetValue(model, DateTime.Now);
            }
            if (this.DbFields != null && this.DbFields.Count > 0)
            {
                foreach (var item in this.DbFields)
                {
                    if ((item.IsPrimaryKey && item.Identifier) || !item.Insert)
                        continue;
                    if (item.IsEnabled)
                    {
                        var fieldProperty = this.EntityFields.Where(x => x.Name.Equals(item.Name)).FirstOrDefault();
                        var value = fieldProperty.GetValue(model);
                        Type fieldtype = Nullable.GetUnderlyingType(fieldProperty.PropertyType) ?? fieldProperty.PropertyType;
                        if (item.AllowDBNull && value == null)
                        {
                            continue;
                        }
                        else if (item.AllowDBNull 
                            && fieldtype == typeof(DateTime) 
                            && (value.ToDateNull() == DateTime.MinValue 
                            || value.ToDateNull() == DateTime.MaxValue))
                        {
                            continue;
                        }
                            if (index > 0)
                        {
                            strsql.Append(",");
                            strParam.Append(",");
                        }
                        string parameterName = parameter.GetParameter(item.Name);
                        strsql.Append(item.Name);
                        strParam.Append(parameterName);
                        index++;
                        if (!item.AllowDBNull && value == null)
                        {
                            throw new Exception(item.Name + "不允许为空");
                        }

                        parameter.Add(new DataParameter(item.Name, (item.AllowDBNull && value == null) ? DBNull.Value : value));
                    }
                }
            }
            else
            {
                foreach (var fieldProperty in this.EntityFields)
                {
                    var value = fieldProperty.GetValue(model);

                    if (index > 0)
                    {
                        strsql.Append(",");
                        strParam.Append(",");
                    }
                    string parameterName = parameter.GetParameter(fieldProperty.Name);
                    strsql.Append(fieldProperty.Name);
                    strParam.Append(parameterName);
                    index++;
                    parameter.Add(new DataParameter(fieldProperty.Name, (value == null) ? DBNull.Value : value));
                }
            }
            strsql.Append(")");
            if (this.Identifier && dbRepository.DbProviderType == ProviderType.SqlServer)
            {
                strsql.Append(dbRepository.IdentitySql(this.PrimaryKeyName));
            }
            strParam.Append(")");
            strsql.Append(strParam.ToString());
            if (this.Identifier && dbRepository.DbProviderType != ProviderType.SqlServer)
            {
                strsql.Append(";" + dbRepository.IdentitySql(""));
            }
            return strsql.ToString();
        }

        /// <summary>
        /// 删除Sql
        /// </summary>
        /// <param name="dbEntity"></param>
        /// <param name="parameters"></param>
        /// <param name="isupdated"></param>
        /// <returns></returns>
        public string DeleteSql(ref DataParameterCollection parameters)
        {
            StringBuilder strsql = new StringBuilder();
            if (this.TableAttr != null && !this.TableAttr.IsDeleted)
            {
                strsql.Append(" delete  from " + this.TableName);
            }
            else
            {
                strsql.Append(" update " + this.TableName + " set " + EntityBase.__IsDeleted + "=1 ");
                if (this.TableAttr != null && this.TableAttr.IsLastModify)
                {
                    strsql.Append("," + EntityBase.__LastModifyDate + "=" + parameters.GetParameter(EntityBase.__LastModifyDate));
                    strsql.Append("," + EntityBase.__LastModifyUserId + "=" + parameters.GetParameter(EntityBase.__LastModifyUserId));
                    parameters.Add(EntityBase.__LastModifyDate, DateTime.Now);
                    parameters.Add(EntityBase.__LastModifyUserId, ApplicationEnvironments.DefaultSession.UserId);
                }
            }
            strsql.Append("  where 1=1 {0} ");
            return strsql.ToString();
        }

        /// <summary>
        /// 获取updatesql
        /// </summary>
        /// <param name="model"></param>
        /// <param name="parameter"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        public string UpdateSql(object model, ref DataParameterCollection parameter, ref string strError)
        {
            return UpdateSql(model, null, ref parameter, ref strError);
        }
        /// <summary>
        /// 生成update sql语句
        /// </summary>
        /// <param name="model">实体</param>
        /// <param name="criterion">条件</param>
        /// <param name="parameter">参数</param>
        /// <param name="strError"></param>
        /// <returns></returns>
        public string UpdateSql(object model,ICriterion criterion, ref DataParameterCollection parameter, ref string strError)
        {

            StringBuilder strsql = new StringBuilder();
            strsql.Append(" update " + this.TableName + " set ");

            int index = 0;

            //if (DbEntityMap.DataCollection == null || DbEntityMap.DataCollection.Count <= 0)
            //{
            //    strError = "没有需要更新的数据";
            //    return "";
            //}
            if (model is EntityBase)
            {
                var dbMap = model as EntityBase;
                if (this.TableAttr != null && this.TableAttr.IsLastModify)
                {
                    dbMap.LastModifyUserId= ApplicationEnvironments.DefaultSession.UserId;
                    dbMap.LastModifyDate= DateTime.Now;
                }
                if (this.DbFields != null && this.DbFields.Count > 0)
                {
                    foreach (var item in dbMap.DataCollection)
                    {
                        var dbField = this.DbFields.Where(x => x.Name.ToLower().Equals(item.Key.ToLower())).FirstOrDefault();

                        if (dbField == null || !dbField.Update || !dbField.IsEnabled || dbField.IsPrimaryKey)
                        {
                            continue;
                        }
                        if (index > 0)
                        {
                            strsql.Append(",");
                        }
                        var fieldProperty = this.EntityFields.Where(x => x.Name.Equals(item.Key)).FirstOrDefault();
                        var value = fieldProperty.GetValue(model);
                        string parameterName = parameter.GetParameter(item.Key);
                        strsql.Append(item.Key + "=" + parameterName);
                        index++;
                        parameter.Add(new DataParameter(item.Key, (dbField.AllowDBNull && value == null) ? DBNull.Value : value));
                    }
                }
            }
            else
            {
                foreach (var field in this.EntityFields)
                {
                    if (this.PrimaryKeyName == field.Name)
                    {
                        continue;
                    }
                    if (index > 0)
                    {
                        strsql.Append(",");
                    }

                    var value = field.GetValue(model);
                    string parameterName = parameter.GetParameter(field.Name);
                    strsql.Append(field.Name + "=" + parameterName);
                    index++;
                    parameter.Add(new DataParameter(field.Name, (value == null) ? DBNull.Value : value));
                }
            }
            if (criterion == null)
            {
                strsql.Append(" where " + this.PrimaryKeyName + "=" + parameter.GetParameter(this.PrimaryKeyName));
                parameter.Add(new DataParameter(this.PrimaryKeyName, GetPrimaryValue(this.PrimaryKeyName, model)));
            }
            else
            {
                strsql.Append(" where " + criterion.ToString(ref parameter));
            }
          
            return strsql.ToString();
        }

     
        /// <summary>
        /// 获取实体中主键的值
        /// </summary>
        /// <param name="model">实体</param>
        /// <returns></returns>
        public object GetPrimaryValue(object model)
        {
            return this.GetPrimaryValue(this.PrimaryKeyName, model);
        }
        /// <summary>
        /// 获取实体中主键的值
        /// </summary>
        /// <param name="keyName">主键值</param>
        /// <param name="model">实体</param>
        /// <returns></returns>
        public object GetPrimaryValue(string keyName,object model)
        {
            if (string.IsNullOrWhiteSpace(keyName))
            {
                return null;
            }
            var attr = this.EntityFields.Where(x => x.Name.Equals(keyName)).FirstOrDefault();
            if (attr != null)
            {
                return attr.GetValue(model);
            }
            return null;
        }
        #endregion


        #region Insert Update Delete Load
        /// <summary>
        /// 插入数据到数据库
        /// </summary>
        /// <param name="entity">实体类</param>
        public bool Insert(EntityBase dbMap, ref string strError)
        {
            IDbRepository dbRepository = CreateDataAccessComponent(dbMap.DbRepository);
            DataParameterCollection dbParameters = dbRepository.GetDbParameters();
            string insertSql = InsertSql(dbRepository, dbMap, ref dbParameters);
            if (string.IsNullOrWhiteSpace(insertSql))
            {
                strError = "插入SQL有误";
                return false;
            }
            try
            {
                return dbRepository.Execute(insertSql, dbParameters) > 0;
            }
            catch (Exception ex)
            {
                strError = ex.StackTrace + ex.Message;
            }
            return false;
        }

        /// <summary>
        /// 更新数据到数据库
        /// </summary>
        /// <param name="entity">实体类</param>
        public bool Update(EntityBase dbMap, ref string strError)
        {
            IDbRepository dbRepository = CreateDataAccessComponent(dbMap.DbRepository);
            DataParameterCollection dbParameters = dbRepository.GetDbParameters();

            string updateSql = UpdateSql(dbMap, ref dbParameters, ref strError);
            if (string.IsNullOrWhiteSpace(updateSql))
            {
                return false;
            }
            try
            {
                return dbRepository.Execute(updateSql, dbParameters) > 0;
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        /// <summary>
        /// 根据查询条件删除
        /// </summary>
        /// <param name="entity">实体类</param>
        /// <param name="query">查询条件</param>
        public bool Delete(EntityBase dbMap, ICriterion criterion, ref string strError)
        {
            IDbRepository dbRepository = CreateDataAccessComponent(dbMap.DbRepository);
            var dbParameters = dbRepository.GetDbParameters();
            var sqlCommand = DeleteSql( ref dbParameters);
            if (criterion == null)
            {
                object id = GetPrimaryValue(dbMap);
                if (id == null || string.IsNullOrWhiteSpace(id.ToString()))
                {
                    strError = "无条件时 主键不能为空";
                    return false;
                }
                criterion = Expression.Eq(this.PrimaryKeyName, id);
            }
            sqlCommand = string.Format(sqlCommand, " and " + criterion.ToString(ref dbParameters));
            try
            {
                return dbRepository.Execute(sqlCommand, dbParameters) > 0;
            }
            catch (Exception ex)
            {
                strError = ex.StackTrace + ex.Message;
            }
            return false;
        }

        /// <summary>
        /// 该实体类的更新状态
        /// </summary>
        /// <param name="entity">实体</param>
        public EntityState IsUpdate(object entity)
        {
            if (entity is EntityBase)
            {
                return (entity as EntityBase).State;
            }
            return EntityState.None;
        }

        /// <summary>
        /// 该实体类在数据库中是否存在
        /// </summary>
        /// <param name="dbRepository">数据仓储</param>
        /// <returns>true:存在 false:不存在</returns>
        public bool IsExist(IDbRepository repository,object model)
        {
            IDbRepository dbRepository = CreateDataAccessComponent(repository);
            var dbParameters = dbRepository.GetDbParameters();
            var sqlCommand = GetPrimaryQueryString(model, ref dbParameters);
            if (string.IsNullOrWhiteSpace(sqlCommand))
            {
                throw new Exception("构建验证主键是否存在语句失败");
            }
            object objValue = dbRepository.Scalar(sqlCommand, dbParameters);
            if (objValue != null && !string.IsNullOrWhiteSpace(objValue.ToString()))
            {
                return true;
            }
            return false;
           
        }

        /// <summary>
        /// 根据条件,装载数据
        /// </summary>
        /// <param name="entity">实体类</param>
        /// <param name="queryString">查询条件</param>
        /// <returns></returns>
        public bool Load(EntityBase dbMap, ICriterion criterion, ref string strError)
        {
            IDbRepository dbRepository = CreateDataAccessComponent(dbMap.DbRepository);
            var dbParameters = dbRepository.GetDbParameters();
            var sqlCommand = SelectSql();

            if (criterion == null)
            {
                strError = "无条件时 主键不能为空";
                return false;
            }
            sqlCommand = string.Format(sqlCommand, " and " + criterion.ToString(ref dbParameters));
            var idataReader = dbRepository.Reader(sqlCommand, dbParameters);
            try
            {
                dbMap.State = EntityState.Add;
                if (idataReader.Read())
                {
                    DataReaderToEntity(dbMap, idataReader);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                idataReader.Close();
            }
            return true;
        }



        #region 实体绑定
        private void DataReaderToEntity(EntityBase dbMap, IDataReader reader)
        {
            if (reader != null)
            {
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    var value = reader.GetValue(i);
                    if (value != DBNull.Value)
                    {
                        var fieldProperty = this.EntityFields.Where(x => x.Name.Equals(reader.GetName(i))).FirstOrDefault();
                        fieldProperty.SetValue(dbMap, value);
                    }
                }

                dbMap.State = EntityState.Update;
                dbMap.DataCollection.Clear();
            }
        }
        public T DataReaderToEntity<T>(IDataReader reader) where T : new()
        {
            T entity = new T();
            if (reader != null)
            {
                if (entity is Dictionary<string, object>)
                {
                    var obj = entity as Dictionary<string, object>;
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var value = reader.GetValue(i);
                        if (value != DBNull.Value)
                        {
                            obj.Add(reader.GetName(i), value);
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var attr = this.EntityFields.Where(x => x.Name == reader.GetName(i)).FirstOrDefault();
                        if (attr != null)
                        {
                            var value = reader.GetValue(i);
                            if (value != DBNull.Value)
                            {
                                attr.SetValue(entity, value);
                            }
                        }
                    }
                }
            }
            if (entity != null && entity is EntityBase)
            {
                var entityClass = (entity as EntityBase);
                entityClass.State = EntityState.Update;
                entityClass.DataCollection.Clear();
            }
            return entity;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <returns></returns>
        public static T ReaderToEntity<T>(IDataReader reader) where T : new()
        {
            T entity = new T();
            if (reader != null)
            {
                if (entity is Dictionary<string, object>)
                {
                    var obj = entity as Dictionary<string, object>;
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var value = reader.GetValue(i);
                        obj.Add(reader.GetName(i), value != DBNull.Value ? value : null);
                    }
                }
                else
                {
                    var properties = entity.GetType().GetProperties();
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var attr = properties.Where(x => x.Name == reader.GetName(i)).FirstOrDefault();
                        if (attr != null)
                        {
                            var value = reader.GetValue(i);
                            if (value != DBNull.Value)
                            {

                                if (!string.IsNullOrWhiteSpace(attr.PropertyType.Name) && attr.PropertyType.Name.ToLower().Equals("boolean"))
                                {
                                    attr.SetValue(entity, (value.ToString().Equals("1") || value.ToString().ToLower().Equals("true")));
                                }
                                else
                                {
                                    attr.SetValue(entity, value);
                                }

                            }
                        }
                    }
                }
            }
            if (entity != null && entity is EntityBase)
            {
                var entityClass = (entity as EntityBase);
                entityClass.State = EntityState.Update;
                entityClass.DataCollection.Clear();
            }
            return entity;
        }
        #endregion
        public void Dispose()
        {
          
        }



        #endregion

        #region 框架中的CRUD 不限于本框架 
        /// <summary>
        /// 字段赋值
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="fieldName"></param>
        /// <param name="value"></param>
        public void SetValue(object entity, string fieldName, object value)
        {
            if (entity == null)
            {
                return;
            }
            var attr = this.EntityFields.Where(x => x.Name.Equals(fieldName)).FirstOrDefault();
            if (attr != null && attr.SetMethod != null)
            {
                attr.SetValue(entity, value);
            }
        }


        #endregion
    }
}
